package com.sondertara.joya.core.jdbc.resultset.mapper;

import com.sondertara.common.collection.LinkedCaseInsensitiveMap;
import com.sondertara.common.collection.StringMap;
import com.sondertara.common.math.NumberUtils;
import com.sondertara.joya.core.jdbc.support.DbType;
import com.sondertara.joya.core.jdbc.support.dialect.DialectFactory;
import com.sondertara.joya.core.jdbc.support.meta.ColumnDescription;
import com.sondertara.joya.core.jdbc.support.meta.SchemaTableMeta;
import com.sondertara.joya.core.jdbc.support.meta.TableDescription;
import com.sondertara.joya.exceptions.DbException;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TableMetaExtractor implements ResultSetExtractor<SchemaTableMeta> {

    private final DatabaseMetaData databaseMetaData;

    public TableMetaExtractor(DatabaseMetaData databaseMetaData) {
        this.databaseMetaData = databaseMetaData;
    }

    @Override
    public SchemaTableMeta extract(ResultSet record) {
        try {
            DbType dbType = DialectFactory.getDatabaseType(databaseMetaData);
            ResultSet tables = databaseMetaData.getColumns(null, null, "my_tmp_test", null);
            Map<String, ColumnDescription> columnDescriptionMap = new LinkedCaseInsensitiveMap<>();
            MapRowMapper mapRowMapper = new MapRowMapper();
            while (tables.next()) {
                Map<String, Object> map = mapRowMapper.map(tables);
                StringMap stringMap = new StringMap(map);
                ColumnDescription columnMeta = new ColumnDescription();
                String columnName = stringMap.get("COLUMN_NAME");
                columnMeta.setColumnName(columnName);
                columnMeta.setLabelName(columnName);//IS_AUTOINCREMENT
                columnMeta.setColumnTypeName(stringMap.get("TYPE_NAME"));
//                columnMeta.setColumnClassName("");
                columnMeta.setColumnType(Integer.parseInt(stringMap.get("DATA_TYPE")));
//                columnMeta.setScaleSize(0);
                columnMeta.setPrecisionSize(0);
                columnMeta.setOrdinalPosition(Integer.valueOf(stringMap.get("ORDINAL_POSITION")));
                columnMeta.setCharOctetLength(NumberUtils.toInt(stringMap.get("CHAR_OCTET_LENGTH")));
                columnMeta.setDisplaySize(Integer.valueOf(stringMap.get("COLUMN_SIZE")));
                columnMeta.setAutoIncrement("YES".equalsIgnoreCase(stringMap.get("IS_AUTOINCREMENT")));
                columnMeta.setNullable("YES".equalsIgnoreCase(stringMap.get("IS_NULLABLE")));
                columnMeta.setRemarks(stringMap.get("REMARKS"));
                columnMeta.setDefaultValue(stringMap.get("COLUMN_DEF"));
                columnMeta.setSigned(false);
                columnMeta.setPk(false);
                columnMeta.setDbType(dbType);
                columnDescriptionMap.put(columnName, columnMeta);
            }
            ResultSetMetaData metaData = record.getMetaData();
            String tableName = metaData.getTableName(1);
            String schemaName = metaData.getSchemaName(1);
            SchemaTableMeta tableMeta = new SchemaTableMeta();
            tableMeta.setSchemaName(schemaName);
            tableMeta.setTableName(tableName);
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                String columnName = metaData.getColumnName(i);
                String columnLabel = metaData.getColumnLabel(i);
                int columnType = metaData.getColumnType(i);
                String columnTypeName = metaData.getColumnTypeName(i);
                ColumnDescription columnMeta = columnDescriptionMap.get(columnName);
                if (null == columnMeta) {
                    continue;
                }
                columnMeta.setColumnName(columnName);
                if (0 != columnType) {
                    columnMeta.setColumnTypeName(columnTypeName);
                    columnMeta.setColumnClassName(metaData.getColumnClassName(i));
                    columnMeta.setDisplaySize(metaData.getColumnDisplaySize(i));
                    columnMeta.setPrecisionSize(metaData.getPrecision(i));
                    columnMeta.setScaleSize(metaData.getScale(i));
                    columnMeta.setAutoIncrement(metaData.isAutoIncrement(i));
                    columnMeta.setNullable(metaData.isNullable(i) != ResultSetMetaData.columnNoNulls);
                } else {
                    // 处理视图中NULL as fieldName的情况
                    columnMeta.setColumnTypeName("CHAR");
                    columnMeta.setColumnClassName(String.class.getName());
                    columnMeta.setDisplaySize(1);
                    columnMeta.setPrecisionSize(1);
                    columnMeta.setScaleSize(0);
                    columnMeta.setAutoIncrement(false);
                    columnMeta.setNullable(true);
                }
                columnMeta.setLabelName(columnLabel);

                boolean signed = false;
                try {
                    signed = metaData.isSigned(i);
                } catch (Exception ignored) {
                    // This JDBC Driver doesn't support the isSigned method
                    // nothing more we can do here by catch the exception.
                }
                columnMeta.setSigned(signed);
            }
            tableMeta.setColumns(columnDescriptionMap.values().stream().sorted(Comparator.comparing(ColumnDescription::getOrdinalPosition)).collect(Collectors.toList()));
            return tableMeta;
        } catch (SQLException e) {
            throw new DbException(e);
        }
    }
}
